#include <Storage.hpp>

namespace FTSS {
	/**
	  * @brief Opening storage with writing headers to file
	  * @param filename - pointer to char file name
	  * @param flag - 0 if vector of white spaces, 1 if data vector
	  */
	void  Storage::OpenStorage(CCHAR_P filename, const INT_32 flag){
		INT_32 descriptor = open (filename , O_RDWR | O_CREAT, 0644); 
		INT_32 current_size;
		INT_64 current_size_from_file;
		INT_64 *current_mmap;
		// file information
		struct stat oFileStat;
		if (stat(filename, &oFileStat) == -1){
			throw UnixException(errno, "fitat");
		}

		// detect a file to read / write header
		if (flag == 0){
			vector_empty_blocksHandler = descriptor;
			current_size = size_vector_file = sysconf(_SC_PAGE_SIZE);
		} else if (flag == 1) {
			data_storageHandler = descriptor;
			current_size = size_storage_nodes = PAGES_FILE_CREATION_NUMBER *  sysconf(_SC_PAGE_SIZE);
		}else throw LogicException("unknow flag value OpenStorage B PLUS TREE");

		// read MAGIC NUMBER or WRITE it in new file;
		if (oFileStat.st_size != 0){
			current_size = oFileStat.st_size;
			current_mmap = (INT_64 *)mmap (0, current_size, PROT_READ | PROT_WRITE, MAP_SHARED,  descriptor, 0);
			if (current_mmap[0] != C_FTSS_MAGIC) throw LogicException("Magic FTSS number is incorrect.");
			current_size_from_file = current_mmap[1];
		}else{
			if (lseek(descriptor, current_size - 1  , SEEK_SET) == -1 
						|| write(descriptor , "", 1) != 1 )
					throw UnixException(errno, "lseek fails");

			current_mmap = (INT_64 *)mmap (0, current_size, PROT_READ | PROT_WRITE, MAP_SHARED,  descriptor, 0);

			if (current_mmap == MAP_FAILED) throw UnixException(errno, "mmap filed");

			current_mmap[0] = C_FTSS_MAGIC; 
			if (flag == 0)
				current_size_from_file = current_mmap[1] = 0;
			else 
				current_size_from_file = current_mmap[1] = 1;
		}

		//again flag detection
		if (flag == 0){
			vector_data_mmap = current_mmap;
			length_stack = current_size_from_file;
		}else if (flag == 1){
			length_data = current_size_from_file;
			storage_data_mmap = (CHAR_P) current_mmap;
		}else {
			// something wrong with flags
			throw LogicException("DA FUQ IT WAS?! flag in BPlusTreeNodesHandler OpenStorage"); 
		}
	}

	/**
	  * @brief Constructor
	  */
	Storage::Storage(CCHAR_P storage_name, CCHAR_P white_space_name){
		try {
			length_stack = 0;
			length_data = 1;
			OpenStorage(white_space_name, 0);
			OpenStorage(storage_name, 1);
		} catch (LogicException &e){
			close(vector_empty_blocksHandler);
			close(data_storageHandler);
			throw;
		} catch (UnixException &e){
			close(vector_empty_blocksHandler);
			close(data_storageHandler);
			throw;
		} catch (...) {
			close(vector_empty_blocksHandler);
			close(data_storageHandler);
			throw;
		}
	}

	/**
 	  * @brief Destructor
	  */
	Storage::~Storage() throw(){
		// write data about length with data 
		vector_data_mmap[1] = length_stack;
		INT_64 * storage_data_mmap64 = (INT_64 *)storage_data_mmap;
		storage_data_mmap64[1] = length_data;
		// unmap all vectors
		if (munmap(vector_data_mmap, size_vector_file) == -1){
			fprintf(stderr, "empty vector unmap error : %s\n", strerror(errno));
		}
		if (munmap(storage_data_mmap, size_storage_nodes) == -1){
			fprintf(stderr, "storage nodes unmap error : %s\n", strerror(errno));
		}
		//close files
		close(vector_empty_blocksHandler);
		close(data_storageHandler);
	}

	/**
          * @brief if file size isn't enough add size and remmap it to RAM
          * @param file descriptor
          * @return 0 if success, -1 if something wrong 
          */
	INT_32 Storage::ReMMAP(INT_32 descriptor){
		struct stat oStat;
		if (fstat(descriptor, &oStat) != 0)
			return -1;
		// new size calculation here may be another function
		// здесь вычисляется новый размер файла, может быть иная функция
		INT_32 new_size = oStat.st_size * 2;
		// reserve file new size on HDD
		if (lseek(descriptor, new_size - 1  , SEEK_SET) == -1 
			|| write(descriptor , "", 1) != 1 )
				throw UnixException(errno, "lseek fails");

		// unmap and map with new size
		if (descriptor == vector_empty_blocksHandler){
			// vector of White Spaces
			vector_data_mmap[1] = length_stack;
			if (munmap(vector_data_mmap, size_vector_file) == -1){
				fprintf(stderr, "empty vector unmap error : %s\n", strerror(errno));
				throw UnixException(errno, "empty vector unmap error");
			}
			vector_data_mmap = (INT_64 *)mmap (0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED,  descriptor, 0);
			size_vector_file = new_size;
		} else if (descriptor == data_storageHandler) {
			// vector of data
			*(storage_data_mmap + sizeof(INT_64)) = length_data;
			if (munmap(storage_data_mmap, size_storage_nodes) == -1){
				fprintf(stderr, "nodes storage unmap error : %s\n", strerror(errno));
				throw UnixException(errno, "nodes storage unmap error");
			}
			storage_data_mmap = (CHAR_P)mmap (0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED,  descriptor, 0);
			size_storage_nodes = new_size;
		} else throw LogicException("Remmap error descriptor");
		return 0;
	}

		
}
